package bgpemulation;
import java.util.LinkedList;
import java.util.List;


/** 
 * 
 * 	Each of our 'trusted' AS's will have one of these. Each AS will create one by calling  
 * 	TrustedRoutingRegistry(AS-Number), where it will create a trusted routing registry with the
 * 	AS's number. 
 * 
 * 	Then, the registry needs to be set by first giving it the AS's routing table, which will
 * 	initialize this registry's routing table and graph. This is done by calling 
 * 	createTrustedRegistry(BGPRoutingTable) with the AS's BGP routing table as an argument. 
 * 
 * 	After that, when a trusted AS wants to send its routing information to another trusted AS, 
 * 	it calls that AS's trusted registry's updateTrustedRegistry(AS-number, routing table) using
 * 	its AS number and BGP routing table as arguments. 
 * 
 * 	Potential problems: creating the trusted registry for the first time and how to and how
 * 						often to update it?
 * 
 * 						or, could create the trusted registry on designated update time, then
 * 						supply it with the BGP routing table so it will have an updated table and
 * 						graph, then have the other routing tables sent around in the trusted
 * 						network and let the updates happen. 
 * @author Richard
 *
 */

public class TrustedRoutingRegistry {

	BGPGraph trustedASTopology;
	int asNumber;
	
	BGPRoutingTable trustedASRoutingTable;
	
	public TrustedRoutingRegistry(int asNumber)
	{
		this.asNumber = asNumber;
	}
	
	public BGPGraph getTrustedASTopology()
	{
		return trustedASTopology;
	}
	
	private List<ASPath> getNewASPathList(int currentAS, List<ASPath> asPathList)
	{
		/**
		 * Sanitizing AS path list to get a list of prepared values
		 */
		List<ASPath> newASPathList = new LinkedList<ASPath>();
		for (int i = 0; i < asPathList.size(); i++)
		{
			ASPath currentPath = asPathList.get(i);
			for (int j = i + 1; j < asPathList.size(); j++)
			{
				ASPath nextPath = asPathList.get(j);
				if (areUnique(currentPath.getArray(), nextPath.getArray()))
				{
					//		DO WE NEED TO REMOVE THEM FROM LIST AFTER DETERMINING THEY ARE UNIQUE?
					//	remove currentPath and nextPath from asPathList
					asPathList.remove(currentPath);
					asPathList.remove(nextPath);
					
					//	reverse of nextPath plus currentAS number plus currentPath
					String combinedPath = "";
					for (int index = nextPath.getArray().length - 1; index >= 0; index--)
					{
						combinedPath += nextPath.getArray()[index] + " ";
					}
					combinedPath += currentAS + " ";
					for (int index = 0; index < currentPath.getArray().length; index++)
					{
						combinedPath += currentPath.getArray()[index] + " ";
					}
					newASPathList.add(new ASPath(combinedPath));
				}
			}
		}
		for (ASPath path : asPathList)
		{
			String newPath = "";
			newPath += currentAS + " ";
			newPath += path.toString();
			newASPathList.add(new ASPath(newPath));
		}
		return newASPathList;
	}
	
	private List<Integer> intArrayToList(int[] intArray)
	{
		List<Integer> intList = new LinkedList<Integer>();
		for (int i : intArray)
		{
			intList.add(i);
		}
		
		return intList;
	}
	
	private List<Integer> getASNumList(BGPRoutingTable rt)
	{
		List<Integer> asNumList = new LinkedList<Integer>();
		/**
		 * Entire quad-for loop gets all unique AS-numbers of first routing table
		 */
		// gets keys from rt0.prefixes, which are ints from 1-32
		for (Integer prefixLength : rt.prefixes.keySet())
		{
			for (CIDRPrefix prefix : rt.prefixes.get(prefixLength).keySet())
			{
				//System.out.println("For CIDRPrefix " + prefix.toString());
				for (ASPath path : rt.prefixes.get(prefixLength).get(prefix))
				{
					for (int asNum : path.getArray())
					{
						//System.out.println(asNum);
						if (!asNumList.contains(asNum))
							asNumList.add(asNum);
					}
				}
			}
		}
		return asNumList;
	}
	
	private List<ASPath> getASPathList(BGPRoutingTable rt)
	{
		List<ASPath> asPathList = new LinkedList<ASPath>();
		/**
		 * Gets all paths from the second routing table and stores them into asPathList
		 */
		for (Integer prefixLength : rt.prefixes.keySet())
		{
			for (CIDRPrefix prefix : rt.prefixes.get(prefixLength).keySet())
			{
				//System.out.println("For CIDRPrefix " + prefix.toString());
				for (ASPath path : rt.prefixes.get(prefixLength).get(prefix))
				{
					asPathList.add(path);
				}
			}
		}
		return asPathList;
	}
	
	public static boolean areUnique(int[] first, int[] second)
	{
		/**
		 * Scratch code for testing if arrays are unique
		 */
		boolean unique = true;
		for (int i : first)
		{
			for (int j : second)
			{
				if (i == j)
				{
					unique = false;
					break;
				}
			}
		}
		return unique;
	}
	
	// required methods:
	//			create new routing registry
	//			receive BGP routing table and update routing registry with new BGP routing table
	public void createTrustedRegistry(BGPRoutingTable rt, List<CIDRPrefix> localPrefixes)
	{		
		trustedASTopology = new BGPGraph(asNumber, rt, localPrefixes);
		trustedASRoutingTable = rt;
	}
	
	public void updateTrustedRegistry(int asn, BGPRoutingTable rt)
	{
		int currentAS = asn;
		
		List<Integer> asNumList = getASNumList(trustedASRoutingTable);
		List<ASPath> asPathList = getASPathList(rt);
		
		List<ASPath> newASPathList = getNewASPathList(currentAS, asPathList);
		
		printASNumberList(asNumList);
		printNewList(newASPathList);
		
		// at this point, has: list of 'treated' AS paths, can move on to actually doing stuff with them
		for (int currentASNumber : asNumList)
		{
			for (ASPath path : newASPathList)
			{
//				System.out.println("Checking for " + currentASNumber + " in " + path.toString());
				int[] pathArray = path.getArray();
				List<Integer> pathList = intArrayToList(pathArray);
				
//				System.out.println(pathList.indexOf(currentASNumber));
				
				int sliceIndex = pathList.indexOf(currentASNumber); 
				if (sliceIndex != -1)
				{
					// currentAS was found in pathList, need to do shit with it
					// slice the pathList into two arrays at sliceIndex
					
					// first slice, 0 to sliceIndex
					String slice1 = "";
					String slice2 = "";
					for (int i = 0; i < sliceIndex; i++)
					{
						slice1 += pathList.get(i) + " ";
					}
					
					// second slice, sliceIndex+1 to end-1 
					for (int i = sliceIndex + 1; i < pathList.size(); i++)
					{
						slice2 += pathList.get(i) + " ";
					}
					
//					System.out.println("\tSLICE1 = " + slice1);
//					System.out.println("\tSLICE2 = " + slice2);
					
					if (slice1 != "")
						parsePath(currentASNumber, new ASPath(slice1));
					if (slice2 != "")
						parsePath(currentASNumber, new ASPath(slice2));
				}
			}
		}
		
	}
	
	private void printNewList(List<ASPath> newASPathList) {
		System.out.println("New AS-path:");
		for (ASPath path : newASPathList)
		{
			System.out.println(path.toString());
		}
	}

	private void printASNumberList(List<Integer> asNumList) {
		System.out.println("AS number list: ");
		for (int i : asNumList)
		{
			System.out.print(i + " ");
		}
		System.out.println();
	}

	private void parsePath(int asNumber, ASPath path) {
		
//		System.out.println("Parsing path between " + asNumber + " and " + path.toString());
		
		// TODO Auto-generated method stub
		int[] asp = path.getArray();
		int e1 = asNumber;
		int e2 = asp[0];
		
		trustedASTopology.addEdge(e1, e2);
		for(int i = 1; i<asp.length; i++){
			//System.out.println("iterated once");
			trustedASTopology.addEdge(asp[i-1], asp[i]);
//			System.out.println("\tEdge added between " + asp[i-1] + " and " + asp[i]);
		}
	}

	public BGPRoutingTable getBGPRoutingTable() {
		return trustedASRoutingTable;
	}
}
